package com.hibiscusking.basic.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.hibiscusking.basic.config.BussinessException;
import com.hibiscusking.basic.constant.BaseConstant;
import com.hibiscusking.basic.dto.*;
import com.hibiscusking.basic.jwt.JwtUtils;
import com.hibiscusking.basic.jwt.RsaUtils;
import com.hibiscusking.basic.jwt.UserInfo;
import com.hibiscusking.basic.service.LoginService;
import com.hibiscusking.basic.util.*;
import com.hibiscusking.system.domain.Menu;
import com.hibiscusking.system.mapper.MenuMapper;
import com.hibiscusking.system.mapper.PermissionMapper;
import com.hibiscusking.user.domain.Logininfo;
import com.hibiscusking.user.domain.User;
import com.hibiscusking.user.domain.Wxuser;
import com.hibiscusking.user.mapper.LogininfoMapper;
import com.hibiscusking.user.mapper.UserMapper;
import com.hibiscusking.user.mapper.WxuserMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private PermissionMapper permissionMapperl;
    @Autowired
    private MenuMapper menuMapper;
    /**
     * @date 2022/10/22 17:05
     * 账号密码登录
     */
    @Override
    public Map<String, Object> loginAccount(AccountLoginDto dto) {
        Map<String, Object> map = new HashMap<>();
        // 判断是手机号登录还是邮箱
        if (PhoneFormatUtil.isMobileNO(dto.getUsername())) {
            // 判断账号密码是否正确
            Logininfo logininfo = logininfoMapper.findByPhone(dto);
            this.judgeAccount(dto, logininfo);
            // 存入redis
            String token = UUID.randomUUID().toString();
            map = depositRedis(token, logininfo);
            return map;
        } else {
            // 判断账号密码是否正确
            Logininfo logininfo = logininfoMapper.findByEmail(dto.getUsername());
            this.judgeAccount(dto, logininfo);
            // 存入redis
            String token = UUID.randomUUID().toString();
            map = depositRedis(token, logininfo);
            return map;
        }

    }
    /**
     * @date 2022/10/22 17:05
     * 邮箱验证码登录
     */
    @Override
    public Map<String, Object> emailLoginAccount(EmailAccountLoginDto dto) {
        Logininfo logininfo = logininfoMapper.findByEmail(dto.getEmail());
        if (logininfo == null) {
            throw new BussinessException("账号密码错误");
        }
        String pwd = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.SEND_VERIFICATION_EMAIL, dto.getEmail()));
        if (StringUtils.isBlank(pwd) || !pwd.equalsIgnoreCase(dto.getVerification())){
            throw new BussinessException("账号密码错误");
        }
        String token = UUID.randomUUID().toString();
        // 关键信息置空
        emptyLoginInfo(logininfo);
        // 存入redis
        Map<String, Object> map = depositRedis(token, logininfo);
        return map;
    }
    /**
     * @date 2022/10/22 17:06
     * 微信扫码登录
     */
    @Override
    public AjaxResult WeChatLogin(WechatLoginDto dto) {
        // 向微信接口获取token
        String tokens = HttpUtil.httpGet(String.format(BaseConstant.WeChatLoginURL.WECHAT_LOGIN_GET_TOKEN_URL,
                BaseConstant.WeChatToken.WECHAT_REQUEST_APPID,
                BaseConstant.WeChatToken.WECHAT_REQUEST_SECRET, dto.getCode()));
        if (StringUtils.isBlank(tokens)){
            throw new BussinessException("网络异常，token");
        }
        // 获取微信接口返回的token和openid
        JSONObject parseObject = JSONObject.parseObject(tokens);
        String token = parseObject.getString("access_token");
        String openid = parseObject.getString("openid");
        Wxuser wxuser = wxuserMapper.findByOpenid(openid);
        // 判断是否之前用户有扫码登录过
        if (wxuser != null && wxuser.getUserId() != null) {
            String uuid = UUID.randomUUID().toString();
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
            // 关键信息置空
            emptyLoginInfo(logininfo);
            // 存入redis
            Map<String, Object> map = depositRedis(uuid, logininfo);
            return AjaxResult.me().success(map);
        }
        StringBuffer sb = new StringBuffer();
        sb.append("?accessToken=").append(token).append("&openId=").append(openid);
        return AjaxResult.me().fail("noBinding").setResultObj(sb);
    }
    /**
     * @date 2022/10/23 10:06
     * 微信扫码登录，手机号绑定
     */
    @Override
    public Map<String, Object> wechatPhoneBinder(WechatPhoneBinderDto dto) {
        // 判断参数是否正确，并请求微信接口，获取token
        String userInformation = judgePhone(dto);
        // 把微信用户信息存入实体类
        Wxuser wxuser = JSONObject.parseObject(userInformation, Wxuser.class);
        // 判断微信用户名是否是乱码
        if (ChineseUtill.isMessyCode(wxuser.getNickname())){
            wxuser.setNickname(dto.getPhone());
        }
        // 查找绑定的手机号有没有记录
        User userByPhone = userMapper.findUserByPhone(dto.getPhone());
        Logininfo logininfo = new Logininfo();
        // 判断之前用户是否已经有用手机号注册有账号登录过
        if (userByPhone != null) {
            logininfo = judgeIsRegister(wxuser, userByPhone, logininfo);
        } else {
            // 把手机号赋值给user，并设置默认密码
            User user = dtoConvertUser(dto);
            // 先添加logininfo表
            BeanUtils.copyProperties(user,logininfo); // 把user的信息赋值给loginInfo
            logininfoMapper.add(logininfo);
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
            wxuser.setUserId(user.getId());

            wxuserMapper.add(wxuser);
        }
        String token = UUID.randomUUID().toString();
        // 把关键信息置空
        emptyLoginInfo(logininfo);
        Map<String, Object> map = depositRedis(token, logininfo);
        return map;
    }

    private Logininfo judgeIsRegister(Wxuser wxuser, User userByPhone, Logininfo logininfo) {
        if (userByPhone.getLogininfoId() == null){
            BeanUtils.copyProperties(userByPhone, logininfo);
            logininfoMapper.add(logininfo);
            User user = new User();
            user.setLogininfoId(logininfo.getId());
            userMapper.update(user);
        } else {
            logininfo = logininfoMapper.findById(userByPhone.getLogininfoId());
        }
        wxuser.setUserId(userByPhone.getId());
        wxuserMapper.add(wxuser);
        return logininfo;
    }

    private String judgePhone(WechatPhoneBinderDto dto) {
        // 判断手机号格式是否正确
        if (!PhoneFormatUtil.isMobileNO(dto.getPhone())){
            throw new BussinessException("手机号不正确");
        }
        // 判断验证码是否正确
        String yzm = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.BINDER_SMSCODE_PHONE, dto.getPhone()));
        if (!dto.getVerifyCode().equalsIgnoreCase(yzm)){
            throw new BussinessException("验证码不正确");
        }
        // 获取微信用户信息
        String userInformation = HttpUtil.httpGet(String.format(BaseConstant.WeChatLoginURL.WECHAT_LOGIN_GET_USER_INFORMATION_URL,
                dto.getAccessToken(), dto.getOpenId()));
        if (StringUtils.isBlank(userInformation)){
            throw new BussinessException("网络异常");
        }
        return userInformation;
    }

    private void emptyLoginInfo(Logininfo logininfo) {
        logininfo.setId(null);
        logininfo.setEmail("");
        logininfo.setPassword("");
        logininfo.setPhone(null);
        logininfo.setSalt(null);
    }

    private User dtoConvertUser(WechatPhoneBinderDto dto) {
        String saltValue = StrUtils.getComplexRandomString(32); // 设置盐值
        // 把密码拼接上盐值
        String password = saltValue + 1;
        // 使用md5对密码进行加密
        String pwd = MD5Utils.encrypByMd5(password);
        return User.builder()
                .username(dto.getPhone())
                .phone(dto.getPhone())
                .salt(saltValue)
                .password(pwd)
                .createtime(new Date())
                .state(1)
                .build();
    }
    /**
     *
     * @param uuid redis的key
     * @param logininfo redis的值
     * @return
     * 存入redis的方法
     */
    private Map<String, Object> depositRedis(String uuid, Logininfo logininfo) {
//        redisTemplate.opsForValue().set(uuid, logininfo,30, TimeUnit.MINUTES);
        Map<String, Object> map = new HashMap<>();
        UserInfo userInfo = new UserInfo();
        // 给UserInfo类Logininfo属性赋值
        userInfo.setLogininfo(logininfo);
        if (logininfo.getType() == 0) {
            // 查询当前用户菜单权限有那些
            List<String> sn = permissionMapperl.findByLoginId(logininfo.getId());
            // 查询当前用户按钮权限有那些
            List<Menu> menus = menuMapper.findMenusById(logininfo.getId());
            // 赋值
            userInfo.setMenus(menus);
            userInfo.setPermission(sn);
            map.put("permissions", sn);
            map.put("menus", menus);
        }
        //  获取私钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("jwt/pethome_auth_rsa.pri").getFile());
        // 加密UserInfo生成jwt
        String jwt = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 30);
 
        map.put("token", jwt);
        map.put("logininfo", logininfo);
        return map;
    }

    /**
     * @date 2022/10/22 17:06
     * 判断账号密码方法
     */
    private void judgeAccount(AccountLoginDto dto, Logininfo logininfo) {
        if (logininfo == null) {
            throw new BussinessException("账号密码错误");
        }
        String password = dto.getPassword();
        String salt = logininfo.getSalt();
        String pwd = MD5Utils.encrypByMd5( salt + password);
        if (StringUtils.isBlank(pwd) || !pwd.equals(logininfo.getPassword())) {
            throw new BussinessException("账号密码错误");
        }
    }
}
